`timescale 1ns / 1ps
module ddr2_port(
/**********NAND flash Sync mode Port*********/
    output      WE_s,  
    output      RE_dif,     
    output      CLE,   
    output      ALE,   
    inout[7:0]  DQ,     
    inout       DQS_dif,    
    input       RB,     
/***********Mode control port**************/
    input       clkX2,
    input       dclk,
    input       rst_n,
    input       en,         //port module enable
    input[2:0]  Cmd,  //
    input[7:0]  InputVal,       //
    output      IV_En,
    output[7:0] ReadVal,
    output      RV_En,
    input[31:0] RWDataNum,
    input[31:0] RWDataCounter
);

parameter tDBS =   16'h0002;
parameter tRPRE =  16'h0008;
parameter tCDQSS = 16'h0008;
parameter tWPRE =  16'h0008;

parameter State_command =   3'b000;
parameter State_adderss =   3'b001;
parameter State_WriteData = 3'b010;
parameter State_ReadData  = 3'b011;
parameter State_Idle =      3'b100;
parameter State_WriteWait = 3'b111;

wire     input_data_en_w;
reg      input_data_en_r;
reg      read_data_vaild;
reg[7:0] read_val;

assign IV_En = en ? input_data_en_w : 1'bz;
assign RV_En = en ? read_data_vaild : 1'bz;
assign ReadVal = en ? read_val : 8'hzz;

wire        clk_m;

wire        we_n_w;
wire        re_n_w;
reg         re_n_val;
reg         cle_r;
reg         ale_r;
wire[7:0]   dq_io_w;
wire[7:0]   dq_i_w;
reg [7:0]   dq_o_r;
reg         read_en;
reg         wp_n_r;
wire        dqs_io;
wire        dqs_i;
wire        dqs_o;
reg         dqs_val;
reg[7:0]    dq_ca;
reg[7:0]    dq_data;

assign WE_s = en ? we_n_w : 1'bz;
assign RE_dif = en ? re_n_w : 1'bz;
assign CLE = en ? cle_r : 1'bz;
assign ALE = en ? ale_r : 1'bz;
assign DQ = en ? dq_io_w : 8'hzz;
assign dq_i = dq_io_w;
assign dq_io_w = read_en ? 8'hzz : dq_o_r;
assign DQS_dif = en ? dqs_io : 1'bz;
assign dqs_i = dqs_io;
assign dqs_io = read_en ? 1'bz :dqs_o;
assign clk_m = clkX2;

reg we_clk_en;
reg re_clk_en;
reg dqs_auto_en;
reg re_n_auto_en;
reg ien;
wire ien_w;

assign we_n_w = we_clk_en ? dclk : 1'b1;
assign re_n_w = re_n_auto_en ? dclk : re_n_val;
assign dqs_o = dqs_auto_en ? dclk : dqs_val;
assign input_data_en_w = (Cmd === State_WriteData | Cmd == State_WriteWait) ? input_data_en_r : ien_w;
assign ien_w = ien ? dclk : 1'b0;

always @(*) begin
    if (Cmd === State_WriteData | Cmd === State_WriteWait) begin
        dq_o_r = dq_data;
    end else if(Cmd === State_command | Cmd === State_adderss) begin
        dq_o_r = dq_ca;
    end else begin
        dq_o_r = dq_o_r;
    end
end

reg[2:0] last_cmd;
always @(posedge dclk or negedge rst_n) begin
    if (rst_n) begin// 
        if (Cmd === State_Idle & last_cmd === State_Idle | Cmd === State_ReadData) begin
            ien <= 1'b0;
        end else begin
            ien <= 1'b1;
        end
        last_cmd <= Cmd;
    end else begin
        ien <= 1'b0;
        last_cmd <= State_Idle;
    end
end

always @(posedge clk_m or negedge rst_n) begin
    if(rst_n != 1'b0) begin
        if (Cmd === State_ReadData) begin
            read_en <= 1'b1;
        end else begin
            read_en <= 1'b0;
        end
    end else begin
        read_en <=  1'b0;
    end
end

always @(negedge dclk or negedge rst_n) begin
    if (rst_n) begin
        if (Cmd === State_adderss) begin
            ale_r <= 1'b1;
        end else begin
            ale_r <= 1'b0;
        end
        if (Cmd === State_command & ien) begin
            cle_r <= 1'b1;
        end else begin
            cle_r <= 1'b0;
        end
        if (Cmd === State_Idle) begin
            cle_r <= 1'b1;
            ale_r <= 1'b1;
        end
    end else begin
        cle_r <= 1'b0;
        ale_r <= 1'b0;        
    end
end

always @(posedge clk_m or negedge rst_n) begin
    if (rst_n) begin
        if(Cmd == State_command | Cmd == State_adderss)
            we_clk_en <= 1'b1;
        else 
            we_clk_en <= 1'b0;
    end else begin
        we_clk_en <= 1'b0;
    end
end

/***************write cmd or address***********************/

always @(negedge WE_s) begin
    if (Cmd == State_command | Cmd == State_adderss) begin
        dq_ca <= InputVal;
    end else begin
        dq_ca <= dq_ca;
    end
end

/*******************read data****************************/
reg[15:0] re_counter;
reg       re_counter_inc;
reg       re_started;

always @(posedge clk_m) begin
    if (Cmd === State_ReadData) begin
        if (re_counter === tDBS) begin
            re_n_val <= 1'b0;
            re_n_auto_en <= 1'b0;
            re_started <= 1'b0;
        end else if (re_counter === tDBS + tRPRE) begin
            re_n_val <= 1'b1;
            re_n_auto_en <= 1'b1;
            re_started <= 1'b1;
            re_counter_inc <= 1'b0;
            re_counter <= re_counter + 1'b1;
        end else if (RWDataCounter === RWDataNum - 8'h2) begin
            re_n_val <= 1'b0;
            re_n_auto_en <= 1'b0;
            re_started <= 1'b1;
        end else if (RWDataCounter >= RWDataNum) begin
            re_n_val <= 1'b0;
            re_n_auto_en <= 1'b0;
        end else begin
            re_n_val <= re_n_val;
            re_n_auto_en <= re_n_auto_en;
            re_started <= re_started;
        end
        re_counter <= re_counter + re_counter_inc;
    end else begin
        re_counter <= 16'h0000;
        re_n_val <= 1'b1;
        re_n_auto_en <= 1'b0;
        re_started <= 1'b0;
        re_counter_inc <= 1'b1;
    end
end

always @(posedge DQS_dif) begin
    if (DQS_dif === 1 & Cmd === State_ReadData) begin
         begin
           read_data_vaild <= 1'b1;
          #1 read_val <= DQ; 
        end 
    end else begin
        read_data_vaild <= 1'b0;
    end
end

always @(negedge DQS_dif) begin
    if (DQS_dif === 0) begin
         begin
           #1 read_val <= DQ; 
        end 
    end
end

/*******************write data****************************/

reg[15:0] dqs_counter;
reg       dqs_counter_inc;
always @(posedge clk_m) begin
    if (Cmd === State_WriteData || Cmd === State_WriteWait) begin
        if (Cmd === State_WriteData) begin
            if (dqs_counter < tCDQSS) begin
                dqs_auto_en <= 1'b0;
                dqs_val <= 1'b1;
            end else if (dqs_counter === tCDQSS + tWPRE - 8'h2)begin
                input_data_en_r <= 1'b1;
            end else if (dqs_counter < tCDQSS + tWPRE) begin
                dqs_auto_en <= 1'b0;
                dqs_val <= 1'b0;
            end else begin
                if (dclk === 1'b0) begin
                    if(input_data_en_r)
                        dqs_auto_en <= 1'b1;
                    input_data_en_r <= 1'b1;
                end else begin
                    dqs_auto_en <= dqs_auto_en;
                    dqs_val <= dqs_val;
                    dqs_counter_inc <= 1'b0;
                end
            end
            dqs_counter <= dqs_counter + dqs_counter_inc;
        end else begin
           dqs_auto_en <= 1'b0;
           dqs_val <= 1'b0;   
           input_data_en_r <= 1'b0;
        end
    end else begin
        dqs_counter_inc <= 1'b1;
        dqs_auto_en <= 1'b0;
        dqs_val <= 1'b0;
        input_data_en_r <= 1'b0;
        dqs_counter <= 16'h0000;
    end
end

always @(negedge clk_m) begin
    if(Cmd === State_WriteWait)begin
        dq_data <= dq_data;
    end else if(Cmd == State_WriteData & input_data_en_r) begin
        dq_data <= InputVal;
    end else begin 
        dq_data <= dq_data;
    end
end

endmodule
